﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace JY.Label.PrintHelper
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class DOCINFOA
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDataType;
    }
    /// <summary>
    /// USB打印帮助类
    /// </summary>
    internal static class UsbPrintHelper
    {
        #region # 打印方法

        #region # 该方法把非托管内存中的字节数组发送到打印机的打印队列
        /// <summary>
        /// 该方法把非托管内存中的字节数组发送到打印机的打印队列
        /// </summary>
        /// <param name="printerName">打印机名称</param>
        /// <param name="pBytes">非托管内存指针</param>
        /// <param name="dwCount">字节数</param>
        /// <returns>成功返回true，失败时为false</returns>
        private static bool SendBytesToPrinter(string printerName, IntPtr pBytes, Int32 dwCount)
        {
            Int32 dwError = 0, dwWritten = 0;
            IntPtr hPrinter = new IntPtr(0);
            DOCINFOA di = new DOCINFOA();
            bool bSuccess = false;

            di.pDocName = "JY.Label.Print";
            di.pDataType = "RAW";

            try
            {
                // 打开打印机
                if (OpenPrinter(printerName.Normalize(), out hPrinter, IntPtr.Zero))
                {
                    // 启动文档打印
                    if (StartDocPrinter(hPrinter, 1, di))
                    {
                        // 开始打印
                        if (StartPagePrinter(hPrinter))
                        {
                            // 向打印机输出字节  
                            bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                            EndPagePrinter(hPrinter);
                        }
                        EndDocPrinter(hPrinter);
                    }
                    ClosePrinter(hPrinter);
                }
                if (bSuccess == false)
                {
                    dwError = Marshal.GetLastWin32Error();
                }
            }
            catch (Win32Exception ex)
            {
                bSuccess = false;
            }
            return bSuccess;
        }
        #endregion

        /// <summary>
        /// 发送文件到打印机方法
        /// </summary>
        /// <param name="printerName">打印机名称</param>
        /// <param name="printFileName">打印的数据</param>
        /// <returns></returns>
        public static bool SendFileToPrinter(string printerName, string printFileName)
        {
            bool bSuccess = false;
            try
            {
                // 打开文件  
                using (FileStream fileStream = new FileStream(printFileName, FileMode.Open))
                {
                    // 将文件内容读作二进制
                    BinaryReader binaryReader = new BinaryReader(fileStream);
                    // 定义字节数组
                    Byte[] bytes = new Byte[fileStream.Length];
                    // 非托管指针  
                    IntPtr pUnmanagedBytes = new IntPtr(0);
                    int nLength;

                    nLength = Convert.ToInt32(fileStream.Length);
                    // 读取文件内容到字节数组
                    bytes = binaryReader.ReadBytes(nLength);
                    // 为这些字节分配一些非托管内存
                    pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
                    // 将托管字节数组复制到非托管内存指针
                    Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
                    pUnmanagedBytes = Marshal.AllocCoTaskMem(bytes.Length);
                    // 将托管字节数组复制到非托管内存指针
                    Marshal.Copy(bytes, 0, pUnmanagedBytes, bytes.Length);
                    // 将非托管字节发送到打印机
                    bSuccess = SendBytesToPrinter(printerName, pUnmanagedBytes, bytes.Length);
                    // 释放先前分配的非托管内存
                    Marshal.FreeCoTaskMem(pUnmanagedBytes);

                    fileStream.Close();
                    fileStream.Dispose();
                }
            }
            catch (Win32Exception ex)
            {
                bSuccess = false;
            }
            return bSuccess;
        }

        /// <summary>
        /// 将字符串发送到打印机方法
        /// </summary>
        /// <param name="printerName">打印机名称</param>
        /// <param name="printData">打印的数据</param>
        /// <param name="encoding">编码方式</param>
        /// <returns></returns>
        public static bool SendStringToPrinter(string printerName, string printData, Encoding encoding)
        {
            bool flag = false;
            try
            {
                //打印的byte数组
                var printBytes = encoding.GetBytes(printData);

                // 非托管指针  
                IntPtr pUnmanagedBytes = Marshal.AllocCoTaskMem(printBytes.Length);

                // 将托管字节数组复制到非托管内存指针
                Marshal.Copy(printBytes, 0, pUnmanagedBytes, printBytes.Length);
                // 将已转换的 ANSI 字符串发送到打印机
                flag = SendBytesToPrinter(printerName, pUnmanagedBytes, printBytes.Length);

                // 释放先前分配的非托管内存
                Marshal.FreeCoTaskMem(pUnmanagedBytes);
            }
            catch (Win32Exception ex)
            {
                flag = false;
            }
            return flag;
        }
        #endregion

        #region # 非托管DLL
        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool EndDocPrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool StartPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool EndPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
        #endregion
    }
}
